/*
 * platforms.h
 *
 *  Created on: 2016年2月21日
 *      Author: guyadong
 */

#ifndef FACEDETECT_FACECL_CONTEXT_H_
#define FACEDETECT_FACECL_CONTEXT_H_
#include <utility>
#include "mycl.h"
#include <iostream>
#include <unordered_map>
#include "cl_utilits.h"
#include "assert_macros.h"

using namespace std;

namespace gdface {

#define KERNEL_NAME(n) #n
#define KERNEL_FILE_NAME(n) #n".cl"
#define _KERNEL_NAME_VAR(name,suffix) kernel_##name##suffix
#define KERNEL_NAME_VAR(name,suffix) _KERNEL_NAME_VAR(name,suffix)
#define _DEF_KERNEL_NAME_VAR(name,suffix) const static std::string _KERNEL_NAME_VAR(name,suffix) (KERNEL_NAME(name)#suffix);
#define DEF_KERNEL_NAME_VAR(name,suffix) _DEF_KERNEL_NAME_VAR(name,suffix)
#define _PREFIX_SUM_SUFFIX(s,t) s##_##t
#define PREFIX_SUM_SUFFIX(s,t) _PREFIX_SUM_SUFFIX(s,t)
DEF_KERNEL_NAME_VAR(image_scaling,)
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_ulong, integ_default))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_ulong, integ_square))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_ulong_ulong, integ_default))

DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_float, integ_default))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_float, integ_square))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_float_float, integ_default))

DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_double, integ_default))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_double, integ_square))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_double_double, integ_default))

DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_uchar_ushort, integ_count))
DEF_KERNEL_NAME_VAR(prefix_sum_col_and_transpose, PREFIX_SUM_SUFFIX(_ushort_ushort, integ_default))

DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_ulong, integ_default))
DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_ulong, integ_square))
DEF_KERNEL_NAME_VAR(integral_scan_v,_ulong)
DEF_KERNEL_NAME_VAR(integral_combine_v,_ulong)
DEF_KERNEL_NAME_VAR(integral_scan_h,_ulong)
DEF_KERNEL_NAME_VAR(integral_combine_h,_ulong)

DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_float, integ_default))
DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_float, integ_square))
DEF_KERNEL_NAME_VAR(integral_scan_v,_float)
DEF_KERNEL_NAME_VAR(integral_combine_v,_float)
DEF_KERNEL_NAME_VAR(integral_scan_h,_float)
DEF_KERNEL_NAME_VAR(integral_combine_h,_float)

DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_double, integ_default))
DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_double, integ_square))
DEF_KERNEL_NAME_VAR(integral_scan_v,_double)
DEF_KERNEL_NAME_VAR(integral_combine_v,_double)
DEF_KERNEL_NAME_VAR(integral_scan_h,_double)
DEF_KERNEL_NAME_VAR(integral_combine_h,_double)

DEF_KERNEL_NAME_VAR(integral_block, PREFIX_SUM_SUFFIX(_uchar_ushort, integ_count))
DEF_KERNEL_NAME_VAR(integral_scan_v,_ushort)
DEF_KERNEL_NAME_VAR(integral_combine_v,_ushort)
DEF_KERNEL_NAME_VAR(integral_scan_h,_ushort)
DEF_KERNEL_NAME_VAR(integral_combine_h,_ushort)


DEF_KERNEL_NAME_VAR(matrix_transpose,_uchar)
DEF_KERNEL_NAME_VAR(haar_filter, )
DEF_KERNEL_NAME_VAR(create_mean, )
DEF_KERNEL_NAME_VAR(create_variance, )
DEF_KERNEL_NAME_VAR(create_hit16, )
DEF_KERNEL_NAME_VAR(create_hit_mat_full, )

DEF_KERNEL_NAME_VAR(intersect_filter, )
DEF_KERNEL_NAME_VAR(object_density_filter, )
DEF_KERNEL_NAME_VAR(object_cluster, )
DEF_KERNEL_NAME_VAR(object_cluster_group, )
/*
 * 全局运行配置参数
 */
struct run_config_type{
#ifdef NDEBUG
		const bool release=true;
#else
		const bool release=false;
#endif
	bool debug_show=false;//是否显示调试界面
	bool kerne_message=false;//是否显示kernel调试信息
};
extern run_config_type global_run_config;
const static std::string  DEFAULT_CL_ROOT = "./cl-files";
#ifdef CL_FILES_EMBEDDED
// 从程序常量区获取头文件名列表
std::vector<const char*> get_headers_from_code();
#define _ADD_HEADERS .add_input_header_files(get_headers_from_code())
#else
// 设置cl源代码根目录位置
#define _ADD_HEADERS .set_source_root(m_source_root)
#endif
class facecl_context {
	cl::Context m_context;
	cl::Device m_device;
	cl::ImageFormat m_gray_format;
	std::unordered_map<std::string,cl::Kernel> m_kernels;
	std::string m_source_root;
	std::pair<size_t, size_t> m_pe_num_per_cu;
	/* 初始化上下文(context),优先获取GPU设备 */
	cl::Context _initContext();
public:	
	void showSupportedImageFormats(cl_mem_flags flags,
			cl_mem_object_type type = CL_MEM_OBJECT_IMAGE2D,
			std::ostream& stream = cout);
	const cl::Context& getContext()const{
		return m_context;
	}
	const cl::Device& getDevice()const {
		return m_device;
	}
	const std::pair<size_t, size_t> getPENumPerCU()const{
		return m_pe_num_per_cu;
	}
	const cl::ImageFormat& getGrayFormat()const{
		return m_gray_format;
	}
	const cl::Kernel& getKernel(const std::string & name) const;
	/*
	 * 返回基于当前设备上下文(cl::Context)创建的新的命令队列
	 * 一般情况下，使用tls::command_queue()函数获取cl::CommandQueue对象，
	 * 以保证所有的kernel函数顺序执行，
	 * 但有如果需要同时执行多个队列的时候，就可以用这个函数获取一个临时的cl::CommandQueue对象
	 * */
	cl::CommandQueue new_command_queue(){
		return cl::CommandQueue(m_context);
	}
	cl_utilits::build_param::builder builder() {
		bool show_kernel_msg=global_run_config.release?false:global_run_config.kerne_message;
		return cl_utilits::build_param::custom(m_context)
								.set_devices({m_device}) // 指定使用第一个OpenCL设备
								.add_define(show_kernel_msg?"DEBUG":"NDEBUG")
								_ADD_HEADERS;
	}
	/* 当前设备(第一个)是否支持double类型 */
	bool double_supported() const{
		return cl_utilits::double_supported(m_device);
	}
	bool device_type_is(cl_device_type device_type)const{
		return device_type==m_device.getInfo<CL_DEVICE_TYPE>();
	}
	facecl_context& createKernels(const cl_utilits::build_param& param);

	inline void addKernel(const std::string &kernel_name,const cl::Kernel &kernel){
#ifndef NDEBUG
		if (this->m_kernels.find(kernel_name) != m_kernels.end()){
			std::cout << "WARNING:duplicated kernel name:"<< kernel_name << std::endl;
		}
#endif
		this->m_kernels.emplace(kernel_name, kernel);
	}
	std::pair<size_t, size_t> get_kernel_preferred_work_group_size_multiple(const std::string& name) const;
	facecl_context()=default;
	facecl_context(const std::string &root);
	virtual ~facecl_context()=default;

};
#undef _ADD_HEADERS
extern facecl_context global_facecl_context;



} /* namespace gdface */

#endif /* FACEDETECT_FACECL_CONTEXT_H_ */
